#include "GameState.h"
#include "physicsBody.h"
#include "GameObjectSystem.h"
#include "glog/logging.h"
#include "ScoreManager.h"
#include <sstream>
#include <string>
#include "ComboControllerProperty.h"
#include "physicsComponent.h"
#include "BulletDynamics/Dynamics/btRigidBody.h"
#include <ctime>
#include "gfxText.h"

const real GameState::SPEED_FACTOR = 10.0f;

void GameState::randomizeBallDirection()
{
	btVector3 velocity = m_ballBody->getBody()->getLinearVelocity();
	btScalar speed = m_ballBody->getMaximumSpeed();
	velocity.setY((real)rand() / RAND_MAX * speed);
	velocity.normalize();
	m_ballBody->editBody()->setLinearVelocity(velocity);

	// IMPORTANT: After this call, the ball has a normalized velocity. You must re-apply the speed for a correct behavior.
}

void GameState::checkCombo(const ObjectId& objectId)
{
	// Get the ComboControllerProperty for a specified object and check if the combo has been completed.
	GameObjectSystem& gameObjectSystem = GameObjectSystem::GetSingleton();
	ObjectProperty* comboProperty = gameObjectSystem.editProperty(ComboControllerProperty::COMBO_CONTROLLER_PROPERTY_ID, objectId);
	if (comboProperty != NULL)
	{
		ComboControllerProperty* combo = static_cast<ComboControllerProperty*>(comboProperty);
		if (combo->isCompleted())
		{
			m_powerShot = true;
			randomizeBallDirection();
		}
		else 
		{
			if (m_powerShot)
				m_powerShot = false;
		}
	}
}

GameState::GameState(const ObjectId& stateId)
	:FSMState(stateId)
	,m_ballBody(NULL)
	,m_ballSpeed(0.f)
	,m_powerShot(false)
	,m_collisionPublisher(NULL)
	,m_collisionSubscriber(NULL)
	,m_uiScore1(NULL)
	,m_uiScore2(NULL)
	,m_uiPowerShot(NULL)
{
	srand((unsigned)time(0));

	GameObjectSystem& gameObjectSystem = GameObjectSystem::GetSingleton();

	ObjectProperty* property = gameObjectSystem.editProperty(PhysicsBody::PHY_BODY_ID, "ball1");
	if(property != NULL)
	{
		m_ballBody= static_cast<PhysicsBody*>(property);
	}
	DLOG_ASSERT(m_ballBody);

	m_collisionSubscriber.SetSubscriber(this);

	// Get the text properties.
	ObjectProperty* score1Property = gameObjectSystem.editProperty(GfxText::GFX_TEXT_ID, "score1");
	if (score1Property != NULL)
	{
		m_uiScore1 = static_cast<GfxText*>(score1Property);
	}
	DLOG_ASSERT(m_uiScore1);

	ObjectProperty* score2Property = gameObjectSystem.editProperty(GfxText::GFX_TEXT_ID, "score2");
	if (score2Property != NULL)
	{
		m_uiScore2 = static_cast<GfxText*>(score2Property);
	}
	DLOG_ASSERT(m_uiScore1);

	ObjectProperty* powerShotProperty = gameObjectSystem.editProperty(GfxText::GFX_TEXT_ID, "info2");
	if (powerShotProperty != NULL)
	{
		m_uiPowerShot = static_cast<GfxText*>(powerShotProperty);
	}
	DLOG_ASSERT(m_uiPowerShot);
}

void GameState::onEnter()
{
	// Set texts.
	m_uiScore1->setText("");
	m_uiScore1->setFontColor(GfxColor::BLUE);
	m_uiScore1->setActive(true);

	m_uiScore2->setText("");
	m_uiScore2->setFontColor(GfxColor::RED);
	m_uiScore2->setActive(true);

	m_uiPowerShot->setText("POWERSHOT!");
	m_uiPowerShot->setFontColor(GfxColor::YELLOW);
	m_uiPowerShot->setActive(true);

	// If it's the first execution, set this state as a subscriber for the physics component.
	if (!m_collisionPublisher)
	{
		GameObjectSystem& gameObjectSystem = GameObjectSystem::GetSingleton();
		Component* cmp = gameObjectSystem.editComponent(PhysicsComponent::PHYSICS_COMPONENT_ID);
		if (cmp != NULL)
		{
			PhysicsComponent* physicsComponent = static_cast<PhysicsComponent*>(cmp);
			m_collisionPublisher = &physicsComponent->getCollisionPublisher();
			m_collisionSubscriber.Subscribe(m_collisionPublisher);
		}
	}

	// Reset the ball speed.
	m_ballSpeed = m_ballBody->getStartSpeed();
	// Power shot isn't active.
	m_powerShot = false;
}

void GameState::onFrame(real frametime, real timestep)
{
	// Read score for the score manager and set the texts.
	ScoreManager& scoreManager = ScoreManager::GetSingleton();
	uint32 p1 = scoreManager.getPlayer1Score();
	uint32 p2 = scoreManager.getPlayer2Score();

	std::stringstream ss1;
	ss1 << p1;
	std::stringstream ss2;
	ss2 << p2;

	std::string s1 = ss1.str();
	std::string s2 = ss2.str();

	m_uiScore1->setText(s1);
	m_uiScore2->setText(s2);

	// Update ball speed.
	if (m_powerShot)
	{
		// If a power shot is active, set the maximum ball speed.
		m_ballBody->setSpeed(m_ballBody->getMaximumSpeed());
		m_uiPowerShot->setActive(true);
	}
	else
	{
		// Increment speed.
		m_ballSpeed += frametime * SPEED_FACTOR;
		m_ballBody->setSpeed(m_ballSpeed);
		m_uiPowerShot->setActive(false);
	}
}

void GameState::onLeave()
{
	// Disable texts.
	m_uiScore1->setActive(false);
	m_uiScore2->setActive(false);
	m_uiPowerShot->setActive(false);
}

void GameState::CollisionEvent(const CollisionData& collision)
{
	GameObjectSystem& gameObjectSystem = GameObjectSystem::GetSingleton();
	ObjectProperty* ball1Property = gameObjectSystem.editProperty(PhysicsBody::PHY_BODY_ID, "ball1");
	ObjectProperty* bump1Property = gameObjectSystem.editProperty(PhysicsBody::PHY_BODY_ID, "bump1");
	ObjectProperty* bump2Property = gameObjectSystem.editProperty(PhysicsBody::PHY_BODY_ID, "bump2");
	if (ball1Property != NULL && bump1Property != NULL && bump2Property != NULL)
	{
		PhysicsBody* ball1Body = static_cast<PhysicsBody*>(ball1Property);
		ObjectId ball1Id = ball1Body->getObjectId();
		PhysicsBody* bump1Body = static_cast<PhysicsBody*>(bump1Property);
		ObjectId bump1Id = bump1Body->getObjectId();
		PhysicsBody* bump2Body = static_cast<PhysicsBody*>(bump2Property);
		ObjectId bump2Id = bump2Body->getObjectId();

		if ((collision.getObjectIdA() == bump1Id || collision.getObjectIdB() == bump1Id) && (collision.getObjectIdA() == ball1Id || collision.getObjectIdB() == ball1Id))
		{
			checkCombo(bump1Id);
		}
		else if ((collision.getObjectIdA() == bump2Id || collision.getObjectIdB() == bump2Id) && (collision.getObjectIdA() == ball1Id || collision.getObjectIdB() == ball1Id))
		{
			checkCombo(bump2Id);
		}
	}
}